#!/usr/bin/perl

####################################################
# Copyright (c) 2002-2007 BioWare Corp.
# http://www.bigdaddymonitor.org
#
# This file is part of the BigDaddy Monitoring
# program. It is free software distributed under
# the terms of the BSD License. It is ditributed
# in the hopes that it will be useful, but
# WITHOUT ANY WARRANTY; without event the implied
# warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
#
# The BSD License is available from:
# http://www.opensource.org/licenses/bsd-license.php
####################################################

#===================================================================================================
#BigDaddy External Monitoring version 3.9
#Author: Duleepa Wijayawardhana <dups@bioware.com>
#Date: 02.25.2004
#Date: 14.04.2006
#===================================================================================================

BEGIN
{
	push(@INC,'/bigdaddy')
}


use strict;
no strict "refs";
use DBI;
use POSIX;
use POSIX qw(:sys_wait_h);
use Config::IniFiles;
use BioMain qw(:allfunctions);
my ($start_val) = @ARGV;

my $processFile="/bigdaddy/logs/external_check.process";

#Before we do anything we are going to bring up the main ini file: bigdaddy.ini should be in the
#same dir as these executables.
my $bigdaddy_cfg= new Config::IniFiles( -file => "/bigdaddy/bigdaddy.ini" );

my $this_process_name = $bigdaddy_cfg->val('main', 'thisprocessname');

my $dbIniHandle;

#Get the processing configuration
my $bdprocessing = $bigdaddy_cfg->val('main', 'bdprocessing');

#Get the dbini handle
#This is dependent on what kind of process is running
if ($bdprocessing eq "single" || $bdprocessing eq "multimaster")
{
	
	$dbIniHandle = $bigdaddy_cfg->val('main', 'dbinihandle');
}
elsif ($bdprocessing eq "masterslave")
{
	if ($bigdaddy_cfg->val('masterslave', 'thisinstance') eq "master")
	{
		$dbIniHandle = $bigdaddy_cfg->val('masterslave', 'masterdbhandle');
	}
	
	if ($bigdaddy_cfg->val('masterslave', 'thisinstance') eq "slave")
	{
		$dbIniHandle = $bigdaddy_cfg->val('masterslave', 'slavedbhandle');
	}
}

my $dbHistoryHandle = $bigdaddy_cfg->val('main', 'historydbhandle');

#Require the common Bigdaddy functions etc.
require ("/bigdaddy/bigdaddy_common.pl");

#Register the subroutines
my %common_mod = (
    "mod_port_tcp.pl" => 1,
    "mod_ping.pl" => 1,
    );

#Require the common modules which are sub routines into the external checker
require ("/bigdaddy/modules/mod_common.pl");


#===================================================================================================
# Main
#===================================================================================================
if ($start_val eq "stop")
{
	system ("killall external_check > /dev/null 2>&1");
	exit;

}

#$SIG{CHLD} = sub { 1 while 0 < waitpid -1, WNOHANG };
&daemonize;
#make a child

my $bReturn=0;
my $nScriptRunTime;

START: while(1)
{
	my $nStartTime=time();
	
	my $Ini="servermon";
	my $sID = "excheck";
	
	#===================================================================================================
	# Processing configuration
	#===================================================================================================
	if ($bdprocessing ne "single")
	{
		if ($bdprocessing eq "masterslave")
		{
			$bReturn = CheckMasterSlave($bigdaddy_cfg, $sID);
		}
		
		if ($bdprocessing eq "multimaster")
		{
			$bReturn = CheckMultiMaster($bigdaddy_cfg, $dbIniHandle, $this_process_name, $sID);
		}
			
		if ($bReturn == 1)
		{
			$nScriptRunTime = time() - $nStartTime;
			#Open a Log File and write in the current status on this server
			&SetProcessTime($processFile);
			sleep(60 - $nScriptRunTime);
			goto START;
		}
		
		if ($bReturn == 2)
		{
			goto CONTINUE;
		}

	}

	#Update the report table.
	CONTINUE:
	
	my $dbb = callfor_db_handle($dbIniHandle, 0, 99);
	
	my $sql="UPDATE report SET report='".time()."', processID = '".$this_process_name."' WHERE ID='".$sID."'";
	$dbb->do( $sql );
	
	#Open a Log File and write in the current status on this server
	&SetProcessTime($processFile);

	$sql="SELECT inifile, ID FROM inifile WHERE ID IN ('".$Ini."')";
	#print $sql."\n";
	my $result=$dbb->prepare( $sql );
	$result->execute;

	while (my $ref=$result->fetchrow_hashref())
	{
	    open (TMPINIFILE, ">/tmp/".$ref->{'ID'}.".ini2") or die "Could not open temp file for writing: $!";
	    print TMPINIFILE $ref->{'inifile'};
	    close (TMPINIFILE) or die "Could not close temp file: $!";
	}

	my $cfg= new Config::IniFiles( -file => "/tmp/".$Ini.".ini2" );

	# Check for the checking delay value
	my $delay=$cfg->val('global','globaldelay');
	my $status=$cfg->val('global','globalstatus');


	if ($status eq "off" || ($status eq "delay" && $delay>time()))
	{
		#continue don't start up external checking
		#Do any processing of off stuff
	}
	else
	{
		$sql="SELECT ID, active FROM server WHERE active IN (1,2)";
		my $result=$dbb->prepare($sql);
		$result->execute;

		my $nServerCount=0;
		while (my $ref=$result->fetchrow_hashref())
		{

			&makeNewChild($ref->{'ID'}, $nServerCount, $ref->{'active'}, %common_mod);

			if ($nServerCount>2)
			{
				$nServerCount=0;
			}
			else
			{
				$nServerCount++;
			}

		}
	}

	#We only want to run this a minimum of every 60 seconds.
	my $nScriptRunTime=time()-$nStartTime;

	$dbb->disconnect;

	if ($nScriptRunTime<60)
	{
		sleep(60-$nScriptRunTime);
	}
}

#===================================================================================================
# Child Subroutine
# Forks child for each cronjob
#===================================================================================================
sub makeNewChild
{

	my ($serverID, $nServerCount, $nServerActive, %common_mod) = @_;
	my $sql;
	my $pid;
	die "fork: $!" unless defined ($pid = fork);


	if ($pid)
	{
		#parent process records the birth of the child and returns to the rest of the script
		#$children[$number]=$pid;
		#my $kid;
		#do
		#{
		#	$kid = waitpid($pid, &WNOHANG);
		#} until $kid > 0;

		#Give it a pause if 3 are running just to make sure we don't overload the server

		#$SIG{CHLD} = 'IGNORE';

		waitpid -1, WNOHANG;

		if ($nServerCount==3)
		{

			sleep (2);
		}
		return;
	}
	else
	{
		waitpid -1, WNOHANG;

		#Create the database handle with no idle and no crash on errors
		my $dbh = callfor_db_handle($dbIniHandle, 0, 99);
		my $dbhistory = callfor_db_handle($dbHistoryHandle, 0, 99);
		
                my $moduleDirectory="/bigdaddy/modules";

		$sql="SELECT sm.*, m.filename, m.type, s.hostname FROM server_module sm
			LEFT JOIN server s ON s.ID=sm.serverID
			LEFT JOIN module m ON m.ID=sm.moduleID
			WHERE sm.serverID=".$serverID." AND m.runfrom=1";
		#print $sql."\n";
		my $result=$dbh->prepare($sql);
		$result->execute;

		while (my $ref=$result->fetchrow_hashref())
		{
			if ($ref->{'active'}==1 || $ref->{'active'}==2)
			{
				my $hostname=$ref->{'hostname'};
				$ref->{'parameters'} =~ s/\[hostname\]//gi;
				$ref->{'parameters'} = &trim($ref->{'parameters'});
				my $result;

				if ($common_mod{$ref->{'filename'}} == 1)
				{
					$ref->{'filename'} =~ m/(.*)\./;
					my $subname = $1;

					$result = &{$subname}($hostname, $ref->{'parameters'});
				}
				else
				{
					my $command=$moduleDirectory."/".$ref->{'filename'}." $hostname ".$ref->{'parameters'};
					$result= `$command`;
				}

				$sql="UPDATE server_module SET lastreport='".$result."', mon_lastreport=".time()." WHERE ID='".$ref->{'ID'}."'";
				$dbh->do( $sql );
				
				if (defined($result) && $result ne '')
				{
					#If it's a boolean, down and it's delayed then we need to report it as "scheduled downtime"
					if (($ref->{'active'}==2 || $nServerActive == 2) && $ref->{'type'} == 1 && ($result eq "0" || $result == 0))
					{
						setServerModuleHistory($ref->{'ID'},$serverID, $ref->{'moduleID'}, "-1", $dbhistory);
					}
					else
					{
						setServerModuleHistory($ref->{'ID'},$serverID, $ref->{'moduleID'}, $result, $dbhistory);
					}
				}
				elsif ($ref->{'type'} == 1 && (!defined($result) || $result eq ''))
				{
					#Some weird no data issue
					setServerModuleHistory($ref->{'ID'},$serverID, $ref->{'moduleID'}, "-3", $dbhistory);
				}
				else
				{
					#no data entered
					setServerModuleHistory($ref->{'ID'},$serverID, $ref->{'moduleID'}, "0", $dbhistory);
				}
				
			}
			
			#If the active state is 0 then enter -2 as the last report
			if ( $ref->{'active'} == 0 && $ref->{'type'} == 1)
			{
				setServerModuleHistory($ref->{'ID'},$serverID, $ref->{'moduleID'}, "-2", $dbhistory);
			}
		}

		$sql="UPDATE server SET last_externalcheck=".time()." WHERE ID='".$serverID."'";
		$dbh->do( $sql );

		exit;
	} #end if
} #end sub

#sub to take care of the server module history
sub setServerModuleHistory
{
	my ($serverModuleID, $serverID, $moduleID, $report, $dbhistory) = @_;
	
	my $sql="INSERT IGNORE INTO server_module_history SET serverModuleID = '".$serverModuleID."', lastreport='".$report."', mon_lastreport=".time().", serverID='".$serverID."', moduleID='".$moduleID."'";
	$dbhistory->do( $sql );
}

